#include "BlockVertexPacked.h"

// lib
#include <glm/glm.hpp>

// std
#include <cassert>

BlockVertexPacked::BlockVertexPacked(const glm::ivec3 &position, const glm::bvec2 &uv)
{
  SetOffset(position.x, position.y, position.z);
  SetUv(uv.x, uv.y);
}

void BlockVertexPacked::SetUv(bool x, bool y)
{
  uint8_t uv = (uint8_t)x | (y << 1);
  assert((((data >> 19) & 0xff) + uv) <= 0xff && "UV Coordinates are out of bounds");

  data += uv << 19;
}

void BlockVertexPacked::SetTexture(uint8_t x, uint8_t y)
{
  const int MaxTextureIndex = 63;
  const int MaxTextureIndexBits = 6;
  assert(x <= MaxTextureIndex && "Coordinate is out of bounds");
  assert(y <= MaxTextureIndex && "Coordinate is out of bounds");

  uint16_t uv = x | (y << MaxTextureIndexBits);
  assert(uv <= (MaxTextureIndex | MaxTextureIndex << MaxTextureIndexBits) && "Texture index is out of bounds");

  data += uv << 21;
}

void BlockVertexPacked::SetAnimSize(uint16_t size)
{
  assert(size <= 127 && "Animation size is out of bounds");
  data2 += size;
}

void BlockVertexPacked::SetOffset(uint32_t x, uint32_t y, uint32_t z)
{
  assert((((data >> 9) & 0x1fu) + x) <= 16 && "Coordinate is out of bounds");
  assert((((data >> 14) & 0x1fu) + z) <= 16 && "Coordinate is out of bounds");
  assert(((data & 0x1ffu) + y) <= 256 && "Coordinate is out of bounds");

  data += y;
  data += x << 9;
  data += z << 14;
}

void BlockVertexPacked::SetSubOffset(uint8_t x, uint8_t y, uint8_t z)
{
  LOG_INFO("{}", 0x1fu);
  LOG_INFO("x: {}", (((subOffset >> 5) & 0x1fu) + x) );
  LOG_INFO("z: {}", (((subOffset >> 10) & 0x1fu) + z) );
  LOG_INFO("y: {}", (((subOffset) & 0x1fu) + y) );

  assert((((subOffset >> 5) & 0x1fu) + x) <= 16 && "Coordinate is out of bounds");
  assert((((subOffset >> 10) & 0x1fu) + z) <= 16 && "Coordinate is out of bounds");
  assert(((subOffset & 0x1fu) + y) <= 16 && "Coordinate is out of bounds");

  subOffset += y;
  subOffset += x << 5;
  subOffset += z << 10;
}

void BlockVertexPacked::SetNormal(const glm::vec3 &normal)
{
  this->normal = normal;
}

void BlockVertexPacked::SetOcclusionLevel(uint8_t occlusionLevel)
{
  assert(occlusionLevel < 4 && "The occlusion level is out of bounds");
  data2 |= occlusionLevel << 8;
}

glm::ivec3 BlockVertexPacked::GetPosition() const
{
  return {(data >> 9) & 0x1fu, data & 0x1ffu, (data >> 14) & 0x1fu};
}
